/**
 * AUTHOR: Zhou Yu <pepsior@163.com>
 *
 * COPYRIGHT(C) 2011 Zhou Yu. ALL RIGHTS RESERVED.
 */
#include <windows.h>
#include <stdio.h>
#include <xl/target.h>
#include <xl/error.h>

/* Read the default path from registry. */
static int xl_target_read_default_path(xl_target_t *target)
{
    HKEY key;
    DWORD bufsize = sizeof(target->path);
    DWORD type = 0;
    BOOL rv = TRUE;

    if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
        L"SOFTWARE\\Thunder Network\\MiniXLOEM\\thunder", 0, KEY_QUERY_VALUE, 
        &key) != ERROR_SUCCESS)
    {
        return XL_EUNACCESSIBLE;
    }

    rv = RegQueryValueEx(key, L"Path", NULL, &type, (LPBYTE)target->path, 
        &bufsize) == ERROR_SUCCESS;
    RegCloseKey(key);

    return rv ? XL_EOK : XL_EUNACCESSIBLE;
}

/* Check the existence of target. */
static int xl_target_exist(const xl_target_t *target)
{
    if(GetFileAttributes(target->path) != INVALID_FILE_ATTRIBUTES)
    {
        return XL_EOK;
    }
    else
    {
        return XL_EUNFOUND;
    }
}

/* Read the version of target. */
static int xl_target_read_version(xl_target_t *target)
{
    DWORD handle = 0;
    DWORD infosize = GetFileVersionInfoSize(target->path, &handle);
    void *infobuf = malloc(infosize);
    VS_FIXEDFILEINFO *fileinfo = NULL;
    UINT fisize = 0;
    BOOL rv = TRUE;

    if(infobuf == NULL)
    {
        return XL_ENOMEM;
    }

    rv = GetFileVersionInfo(target->path, handle, infosize, infobuf)
        && VerQueryValue(infobuf, L"\\", (LPVOID *)&fileinfo, &fisize);

    if(rv)
    {
        xl_version_init(&target->version, HIWORD(fileinfo->dwFileVersionMS), 
            LOWORD(fileinfo->dwFileVersionMS), HIWORD(fileinfo->dwFileVersionLS), 
            LOWORD(fileinfo->dwFileVersionLS));
    }
    
    free(infobuf);
    return rv ? XL_EOK : XL_EUNACCESSIBLE;
}

int xl_target_init(xl_target_t *target, const wchar_t *path)
{
    memset(target, 0, sizeof(target));
    
    if(path == NULL)
    {
        XL_RETE(xl_target_read_default_path(target));
    }
    else
    {
        wcsncpy(target->path, path, MAX_PATH);
    }

    XL_RETE(xl_target_exist(target));
    XL_RETE(xl_target_read_version(target));
    return XL_EOK;
}

int xl_target_patch(const xl_target_t *target, const xl_patch_t *patch)
{
    FILE *fp = _wfopen(target->path, L"rb+");
    size_t offset = xl_patch_get_offset(patch);
    const unsigned char *source = xl_patch_get_source(patch);
    const unsigned char *dest = xl_patch_get_dest(patch);
    unsigned char data[XL_PATCH_LENGTH] = {0};
    int rv = XL_EOK;

    if(fp == NULL)
    {
        return XL_EUNACCESSIBLE;
    }

    if(fseek(fp, offset, SEEK_SET) != 0
        || fread(data, 1, XL_PATCH_LENGTH, fp) != XL_PATCH_LENGTH)
    {
        rv = XL_EUNACCESSIBLE;
        goto end;
    }

    if(data[0] == source[0] 
        && memcmp(&data[1], &source[1], XL_PATCH_LENGTH - 1) == 0)
    {
        fseek(fp, offset, SEEK_SET);

        if(fwrite(dest, 1, XL_PATCH_LENGTH, fp) != XL_PATCH_LENGTH)
        {
            rv = XL_EUNACCESSIBLE;
            goto end;
        }
    }
    else if(data[0] == dest[0]
        && memcmp(&data[1], &dest[1], XL_PATCH_LENGTH - 1) == 0)
    {
        rv = XL_EPATCHED;
    }
    else
    {
        rv = XL_EUNMATCHED;
    }

end:
    fclose(fp);
    return rv;
}
